# app.py
import psycopg2.extras
from flask import Flask, jsonify, send_file, request
import os
import sys
from dotenv import load_dotenv
from datetime import datetime
import shutil
import pandas as pd
import uuid
from ahp_engine import fetch_poi_data, preprocess_data, calculate_ahp_weights, calculate_scores, plot_scores, is_valid_judgment_matrix, generate_pdf_report,DatabaseConnectionPool,PRESET_JUDGMENT_MATRICES
from flask_cors import CORS
import json
import psycopg2
from psycopg2 import extras
from threading import Thread
from utils import PresetMatrixManager, make_response

# 加载.env文件中的环境变量
load_dotenv()

app = Flask(__name__)
# 启用所有域名跨域访问支持
CORS(app)

# 获取当前执行文件（.exe 或 .py）所在目录
if getattr(sys, 'frozen', False):
    # 打包后的路径
    BASE_DIR = os.path.dirname(sys.executable)
else:
    # 开发环境路径
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))

# 读取 .env 中 REPORT_DIR 的值（相对路径）
REPORT_DIR_RELATIVE = os.getenv("REPORT_DIR", "reports/ahp_analysis")
# 拼接为绝对路径
# REPORT_DIR = os.getenv('REPORT_DIR', 'reports/ahp_analysis')
REPORT_DIR = os.path.join(BASE_DIR, REPORT_DIR_RELATIVE)
print("REPORT_DIR",REPORT_DIR)
# 确保路径存在
os.makedirs(REPORT_DIR, exist_ok=True)


def save_analysis_record(data):
    """
    将一次 AHP 分析记录保存到 PostgreSQL 数据库
    
    参数:
        data (dict): 包含分析结果的数据字典
    返回:
        bool: 是否成功保存
    """

    # 从环境变量获取数据库配置
    # db_config = {
    #     "host": os.getenv("DB_HOST"),
    #     "port": int(os.getenv("DB_PORT")),
    #     "database": os.getenv("DB_NAME"),
    #     "user": os.getenv("DB_USER"),
    #     "password": os.getenv("DB_PASSWORD")
    # }

    insert_sql = """
    INSERT INTO public.ahp_analysis_records (
        request_id, user_ip, is_custom_matrix, custom_matrix,
        weights, cr, scores, report_dir, report_zip_url,
        report_pdf_url, ranking_image_url, warning
    ) VALUES (
        %(request_id)s, %(user_ip)s, %(is_custom_matrix)s, %(custom_matrix)s,
        %(weights)s, %(cr)s, %(scores)s, %(report_dir)s, %(report_zip_url)s,
        %(report_pdf_url)s, %(ranking_image_url)s, %(warning)s
    )
    """

    conn = None
    try:
        # with psycopg2.connect(**db_config) as conn:
        #     with conn.cursor() as cursor:
        #         extras.execute_batch(cursor, insert_sql, [data], page_size=100)
        #         conn.commit()
        # return True
        conn = DatabaseConnectionPool.get_connection()
        with conn.cursor() as cursor:
            psycopg2.extras.execute_batch(cursor, insert_sql, [data], page_size=100)
        conn.commit()
        return True
    except Exception as e:
        if conn:
            conn.rollback()
        print(f"[ERROR] 保存分析记录失败: {str(e)}")
        return False
    finally:
        if conn:
            DatabaseConnectionPool.put_connection(conn)

# 合并run_ahp_analysis与run_custom_ahp_analysis函数的重复代码
def _run_ahp_analysis_(judgment_matrix=None,preset=None):
    # Step 0:创建本次分词的独立目录
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    unique_id = str(uuid.uuid4())[:8]
    report_subdir = f"ahp_report_{timestamp}_{unique_id}"
    report_path = os.path.join(REPORT_DIR, report_subdir)

    os.makedirs(report_path, exist_ok=True)
    csv_dir = os.path.join(report_path, 'csv')
    os.makedirs(csv_dir, exist_ok=True)

    # Step 1: 获取原始数据
    raw_df = fetch_poi_data()

    # Step 2: 预处理数据
    original_matrix, normalized_matrix = preprocess_data(raw_df)

    # Step 3: AHP 权重计算
    weights, cr = calculate_ahp_weights(judgment_matrix,preset=preset)

    # Step 4: 综合得分计算
    score_df = calculate_scores(normalized_matrix, weights)

    # Step 5: 导出 CSV 报告
    original_matrix.to_csv(os.path.join(csv_dir, 'original_matrix.csv'), encoding='utf-8-sig')
    normalized_matrix.to_csv(os.path.join(csv_dir, 'normalized_matrix.csv'), encoding='utf-8-sig')
    pd.DataFrame(weights.items(), columns=['POI类型', '权重']).to_csv(os.path.join(csv_dir, 'weights.csv'), encoding='utf-8-sig')
    score_df.to_csv(os.path.join(csv_dir, 'city_scores.csv'), encoding='utf-8-sig')

    # Step 5.5: 绘图
    # 绘图时使用一致的文件名
    image_filename = f"ahp_ranking_{timestamp}_{unique_id}.png"
    image_src_path = os.path.join(report_path, image_filename)

    plot_scores(score_df, image_src_path)  # 保存图片到报告子目录

    # Step 6: 生成PDF报告
    pdf_filename = f"ahp_report_{timestamp}_{unique_id}.pdf"
    pdf_path = os.path.join(report_path, pdf_filename)
    # 直接传入 matrix 到 PDF 生成函数即可
    generate_pdf_report(score_df, weights, image_src_path, original_matrix, normalized_matrix, pdf_path)

    # Step 7: 打包 ZIP
    zip_name = f"ahp_report_{timestamp}_{unique_id}"
    zip_path = os.path.join(REPORT_DIR, zip_name)
    shutil.make_archive(zip_path, 'zip', report_path)

    # Step 8: 返回 JSON 结果
    result = {
        "status": "success",
        "scores": score_df.to_dict(),
        "report_zip": f"/download/{os.path.basename(zip_path)}.zip",
        # "ranking_image": f"/image/{timestamp}_{unique_id}.png"
        "ranking_image": f"/image/{report_subdir}/{image_filename}",
        "report_pdf": f"/download/{report_subdir}/{pdf_filename}",  # 新增字段
        "cr": cr,
        "preset": preset if  preset else 'standard',
    }
    if cr >= 0.1:
        # 为result增加warning警告信息
        result["warning"] = "一致性比率 CR=0.123 > 0.1,建议重新调整判断矩阵以提高一致性."

    # Step 9: 构建记录
    # 预先获取用户IP(考虑代理的情况)
    user_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
    record_data = {
        "request_id": str(uuid.uuid4()),  # 每次请求生成新的 UUID
        "user_ip": request.remote_addr if 'request' in globals() else None,
        "is_custom_matrix": judgment_matrix is not None,
        "custom_matrix": json.dumps(judgment_matrix) if judgment_matrix is not None else None,
        "weights": json.dumps(weights),
        "cr": cr,
        "scores": json.dumps(score_df.to_dict()),
        "report_dir": report_subdir,
        "report_zip_url":  result.get("report_zip"),
        "report_pdf_url": result.get('report_pdf'),
        "ranking_image_url": result.get('ranking_image'),
        "warning": result.get('warning')
    }
    # 异步写入数据库
    saved = save_analysis_record(record_data)
    if not saved:
        print("Failed to save analysis record to database.")

    return result
    # return jsonify(result)


@app.route('/ahp-presets', methods=['GET'])
def get_presets():
    """获取所有可用的预设判断矩阵名称"""
    manager = PresetMatrixManager()
    response = {}
    for key in manager.get_all_keys():
        info = manager.get_preset_info(key)
        info['key'] = key
        response[key] = info
    # return jsonify(response)
    return make_response(status="success", message='预设矩阵列表获取成功', data=response);


@app.route('/ahp-presets/matrix/<key>', methods=['GET'])
def get_ahp_preset_matrix(key):
    """获取用于计算的浮点型矩阵"""
    manager = PresetMatrixManager()
    try:
        matrix = manager.get_matrix(key)
        # return jsonify({"matrix": matrix})
        return make_response(status="success", message=f"预设矩阵 '{key}' 获取成功", data={"matrix": matrix})
    except KeyError as e:
        # return jsonify({"error": str(e)}), 400
        return make_response(code=400, status="error", message=str(e))


@app.route('/ahp-presets/matrix-str/<key>', methods=['GET'])
def get_ahp_preset_matrix_str(key):
    """获取用于展示的字符串矩阵"""
    manager = PresetMatrixManager()
    try:
        matrix_str = manager.get_matrix_str(key)
        # return jsonify({"matrix_str": matrix_str})
        return make_response(status="success", message=f"预设矩阵 '{key}' 获取成功", data={"matrix_str": matrix_str})
    except KeyError as e:
        # return jsonify({"error": str(e)}), 400
        return make_response(code=400, status="error", message=str(e))

@app.route('/ahp-presets/validate', methods=['GET'])
def validate_presets():
    """校验所有矩阵的合法性"""
    manager = PresetMatrixManager()
    invalid_keys = manager.validate_all()
    if invalid_keys:
        # return jsonify({
        #     "status": "warning",
        #     "invalid": invalid_keys
        # })
        return make_response(status="warning", message=f"预设矩阵存在问题~", data={"invalid_keys": invalid_keys})
    else:
        # return jsonify({
        #     "status": "success",
        #     "message": "所有矩阵均合法"
        # })
        return make_response(status="success", message=f"所有矩阵均合法~", data=None)

# ahp层次分析接口
@app.route('/ahp-analysis', methods=['GET'])
def run_ahp_analysis():
    try:
        result = _run_ahp_analysis_()
        return make_response(code=200, status='success', message='AHP分析成功',data=result)
    except Exception as e:
        # return jsonify({"status": "error", "message": str(e)}), 500
        return make_response(code=500, status="error", message=str(e))



# 自定义版本的AHP层次分析接口-支持自定义判断矩阵
@app.route('/custom/ahp-analysis', methods=['POST'])
def run_custom_ahp_analysis():
    try:
        # 获取请求数据
        raw_data = request.get_json(force=True)
        print("Received data:", type(raw_data))
        custom_judgment_matrix = raw_data.get('judgment_matrix')  # 获取用户提供的判断矩阵（可选参数）
        preset_name = raw_data.get('preset')  # 预设名称（如 'hotel_heavy'）
        
        # 判断矩阵校验
        # if custom_judgment_matrix:
        #     if not is_valid_judgment_matrix(custom_judgment_matrix):
        #         return jsonify({"status": "error", "message": "Invalid judgment matrix"}), 400
        result = _run_ahp_analysis_(judgment_matrix=custom_judgment_matrix,preset=preset_name)
        return make_response(code=200, status='success', message='AHP分析成功',data=result)

    except Exception as e:
        # return jsonify({"status": "error", "message": str(e)}), 500
        return make_response(code=500, status="error", message=str(e))


# 下载文件接口
@app.route('/download/<path:filename>', methods=['GET'])
def download_file(filename):
    file_path = os.path.join(REPORT_DIR, filename)
    print("download filePath::",file_path)
    if os.path.exists(file_path):
        return send_file(file_path, as_attachment=True)
    else:
        return jsonify({"status": "error", "message": "File not found"}), 404

# 图片预览接口
@app.route('/image/<path:filename>', methods=['GET'])
def get_image(filename):
    # 构造完整文件路径
    image_path = os.path.join(REPORT_DIR, filename)

    print("image_filePath::",image_path)
    
    # 检查文件是否存在
    if os.path.exists(image_path):
        return send_file(image_path, mimetype='image/png')
    else:
        return jsonify({"status": "error", "message": "Image not found"}), 404

@app.errorhandler(404)
def handle_not_found(e):
    # 返回异常信息
    return make_response(code=404, status="error", message= str(e)|"资源不存在")

@app.errorhandler(500)
def handle_server_error(e):
    return make_response(code=500, status="error", message=str(e)|"服务器内部错误")


if __name__ == '__main__':
    # 根据环境变量执行服务的IP和端口配置
    FLASK_HOST = os.getenv('FLASK_HOST', '0.0.0.0')
    FLASK_PORT = int(os.getenv('FLASK_PORT', 5000))
    FLASK_DEBUG = os.getenv("FLASK_DEBUG", "False").lower() in ["true", "1", "t"]
    # 初始化数据库连接池
    DatabaseConnectionPool.initialize()
    # 启动服务
    # app.run(debug=True, host='0.0.0.0', port=5000)
    app.run(debug=FLASK_DEBUG, host=FLASK_HOST, port=FLASK_PORT)